/**
  ******************************************************************************
  * File Name          : MODBUS.c
  * Auther             : xifeng.yao@qq.com
  * Description        : Code for modbus applications
  ******************************************************************************
  * @attention
  * 包含功能：
  * 1、从机地址 读 & 写;
  * 2、断帧时间计算;
  * 3、数据大小端转译;
  * 4、不同命令码对应不同寄存器接口函数;
  *
  * @special:
  * 
  * 
  ********/
	
/* Includes ------------------------------------------------------------------*/
#include <string.h>

#include "litemodbusBase.h"


/********************
function:  Modbus 断帧倒计时
input:     BaudRate 通道波特率
return:    时间(单位=50us)        e.g.:  [36] x 50us = 1800us
*/
#ifndef Modbus_Ctdw_Calculate
uint16_t Modbus_Ctdw_Calculate(uint32_t BaudRate)
{
  /* If baudrate > 19200 then we should use the fixed timer values
  * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
  */
  if( BaudRate > 19200 )
  {
    return 35;       /* 1800us. */
  }
  else
  {
    /* The timer reload value for a character is given by:
    *
    * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
    *             = 11 * Ticks_per_1s / Baudrate
    *             = 220000 / Baudrate
    * The reload for t3.5 is 1.5 times this value and similary for t3.5.
    */
//    return ( 7UL * 220000UL ) / ( 2UL * BaudRate );
    return ( 770000UL ) / ( BaudRate );
  }
}
#endif

/********************
function:  大端数据转小端数据
input:     1Byte  Modbus_ID
return:
*/
int HSB2LSB(uint8_t* value, uint8_t bitlen)
{
	int result = *value++;   
  result <<= 8;
	
	switch(bitlen)
	{
		case 32:	result |= *value++;   result <<= 8; 
		case 24:	result |= *value++;   result <<= 8; 
		case 16:	result |= *value;
		default:  break;
	}
	return result;
}
/**
  * @brief  ASCII HEX 字符相互转换
  * @param  
  * @retval true or false
  */ 
bool ASCII2HEX(char* input, uint16_t* lenght)
{
  uint16_t i;
  char* output = input;

  for(i=0;i<*lenght;i++)
  {
    *output = 0;
    if((*input>='0')&&(*input<='9'))
    {
      *output |= *input - '0';  
    }else
    if((*input>='a')&&(*input<='z'))
    {
      *output |= *input - 'a' + 10;
    }else
    if((*input>='A')&&(*input<='Z'))
    {
      *output |= *input - 'A' + 10;  
    }else
    {break;}
    
    input++;
    if(i%2)  
    {
      output++;
    }else
    {
      *output <<= 4;
    }
  }
  *lenght = 1 + (i/2);
  return true;
}
bool HEX2ASCII(char* input, uint16_t* lenght)
{
  uint16_t i;
  //都指向最后一个地址
  char* output = &input[1+(*lenght * 2)];
  input += *lenght -1;
  
  for(i=0;i<*lenght * 2;i++)
  { //倒序转换
    *output = *input&0x0F;

    if(*output < 10)
    {
      *output += '0';
    }else
    {
      *output += 'A' - 10;
    }
    output--;
    if(i%2)  
    {
      input--;
    }else
    {
      *input >>= 4;
    }
  }
  *lenght *= 2;
  return true;
}
/**
  * @brief  求累加和
  * @param  
  * @retval true or false
  */ 
uint8_t SUM_int8(char* input, uint16_t lenght)
{
  uint8_t i,result=0;
  
  for(i=0;i<lenght;i++)
  {result += *input++;}
  return result;
}

extern bool Modbus_Coils_Analy(char* data);
extern bool Modbus_Discrete_Analy(char* data);
extern bool Modbus_HoldReg_Analy(char* data);
extern bool Modbus_InputReg_Analy(char* data);
/********************
 @brief:    Modbus 从机模式接收帧解析
 @param:    protocoldata、Length  接收/应答帧 帧长度
            id_check   是否需要进行 id 检测,允许某些条件进行地址通配
 @retval:   true or false (modbus解析正确并完成)
*/
bool Modbus_Slave_Analy(char* protocoldata, uint16_t* Length)
{
  //bool (*ANALYFUNCTION)(ModbusFormat*);
  ModbusFormat* analyBuff = (ModbusFormat*)protocoldata;  //Modbus Slave format

  /*应答帧长度预处理*/
  analyBuff->len = 0;
  
  /*数据类型解析 01 02 03 04 05 06 0f 10*/
  switch(analyBuff->cmd)
  {
    case  CMD_RX_COILS:   //0x01    //读线圈寄存器
                          {memset(analyBuff->data, 0, 8);} //清返回值
                          if(true == Modbus_Coils_Analy(protocoldata))
                          {*Length = (analyBuff->cmd & 0x80)/*返回错误码*/ ? 3 : (3 + protocoldata[2]);   return true;}
                          break;
    case  CMD_TX_COIL:    //0x05    //写单个线圈寄存器
    case  CMD_TX_COILS:   //0x0f    //写多个线圈寄存器
                          if(true == Modbus_Coils_Analy(protocoldata))
                          {*Length = (analyBuff->cmd & 0x80) ? 3 : 6;   return true;}
                          break;
    case  CMD_RX_DISCRETE:  //0x02    //读离散输入寄存器
                            {memset(analyBuff->data, 0, 8);} //清返回值
                            if(true == Modbus_Discrete_Analy(protocoldata))
                            {*Length = (analyBuff->cmd & 0x80) ? 3 : (3 + protocoldata[2]);   return true;} 
                            break;
    case  CMD_RX_HOLDREG:   //0x03    //读保持寄存器
                            if(true == Modbus_HoldReg_Analy(protocoldata))
                            {*Length = (analyBuff->cmd & 0x80) ? 3 : (3 + protocoldata[2]);    return true;} 
                            break;
    case  CMD_TX_HOLDREG:   //0x06    //写单个保持寄存器
    case  CMD_TX_HOLDREGS:  //0x10    //写多个保持寄存器
                            if(true == Modbus_HoldReg_Analy(protocoldata))
                            {*Length = (analyBuff->cmd & 0x80) ? 3 : 6;    return true;} 
                            break;
    case  CMD_RX_INPUTREG:  //0x04    //读输入寄存器
                            if(true == Modbus_InputReg_Analy(protocoldata))
                            {*Length = (analyBuff->cmd & 0x80) ? 3 : (3 + protocoldata[2]);    return true;} 
                            break;

      default:           break;
  }
  return false; //false = 不需要应答回帧
}

